﻿using Newtonsoft.Json;
using System;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Formatting;
using System.Net.Http.Headers;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System.Web.Http;
using Ttifa.Infrastructure;

namespace Ttifa.WebApiBase
{
    /// <summary>
    /// Json/Jsonp 处理结果
    /// </summary>
    public class JsonpMediaTypeFormatter : JsonMediaTypeFormatter
    {
        /// <summary>
        /// 回调JsonCallBack
        /// </summary>
        public string Callback { get; private set; }

        /// <summary>
        /// 
        /// </summary>
        public JsonpMediaTypeFormatter() : this(null) { }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="callback"></param>
        public JsonpMediaTypeFormatter(string callback)
        {
            //设置支持的编码格式
            base.SupportedEncodings.Clear();

            base.SupportedEncodings.Add(Encoding.UTF8);
            base.SupportedEncodings.Add(Encoding.Unicode);
            base.SupportedEncodings.Add(Encoding.Default);

            this.Callback = callback;

            //序列化参数设定
            base.SerializerSettings = DefaultSettings.JsonSerializerSettings;
        }

        public override Task WriteToStreamAsync(Type type, object value, Stream writeStream, HttpContent content, TransportContext transportContext)
        {
            //HTTP错误，直接返回ApiResult
            if (value is HttpError)
            {
                HttpError error = (HttpError)value;
                if (error.InnerException != null) error = error.InnerException;
                StringBuilder sb = new StringBuilder();
                foreach (var ev in error) sb.Append($"{ev.Key}:{ev.Value},");
                var result = new ApiResult(ApiStatus.Fail, sb.ToString().TrimEnd(','));

                return base.WriteToStreamAsync(result.GetType(), result, writeStream, content, transportContext);
            }

            if (string.IsNullOrEmpty(this.Callback))
            {
                return base.WriteToStreamAsync(type, value, writeStream, content, transportContext);
            }

            try
            {
                this.WriteToStream(type, value, writeStream, content);

                return Task.FromResult<AsyncVoid>(new AsyncVoid());
            }
            catch (Exception exception)
            {
                TaskCompletionSource<AsyncVoid> source = new TaskCompletionSource<AsyncVoid>();
                source.SetException(exception);

                return source.Task;
            }
        }

        private void WriteToStream(Type type, object value, Stream writeStream, HttpContent content)
        {
            JsonSerializer serializer = JsonSerializer.Create(this.SerializerSettings);
            using (StreamWriter streamWriter = new StreamWriter(writeStream, this.SupportedEncodings.First()))
            using (JsonTextWriter jsonTextWriter = new JsonTextWriter(streamWriter) { CloseOutput = false })
            {
                jsonTextWriter.WriteRaw(this.Callback + "(");
                serializer.Serialize(jsonTextWriter, value);
                jsonTextWriter.WriteRaw(");");
            }
        }

        public override MediaTypeFormatter GetPerRequestFormatterInstance(Type type, HttpRequestMessage request, MediaTypeHeaderValue mediaType)
        {
            if (request.Method != HttpMethod.Get) return this;
            var callback = string.Empty;
            if (request.GetQueryNameValuePairs().ToDictionary(pair => pair.Key, pair => pair.Value).TryGetValue("callback", out callback))
            {
                return new JsonpMediaTypeFormatter(callback);
            }

            return this;
        }
    }
}